#include <iostream>
#include <string>
#include <mutex>
#include <chrono>
#include <vector>
#include <algorithm>

extern "C" {
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <wiringPi.h>
#include <wiringSerial.h>
#include <pthread.h>
#include <sys/time.h>
}

using std::vector;

enum enGPIO {
	pps_gpio = 0,
};

namespace ISR {
	volatile uint32_t counter = 0;
	volatile int int_flag = 0;
}

void cb_pin_isr(void){
	printf("pps detected\n");
	fflush(stdout);
	ISR::int_flag = 1;
	++ISR::counter;
	return;
}

void update_time(void);

class c_utc_time {

private:
	uint32_t year;
	uint32_t month;
	uint32_t day;
	uint32_t hour;
	uint32_t minute; 
	uint32_t second;
	uint32_t millisecond;
	vector<uint8_t> vec_msg;
	uint8_t msg[21] = {0xbd,0xdb,0x53};

public:

	/* c'tor */
	c_utc_time(uint32_t y , uint32_t mo, uint32_t d, \
			   	uint32_t h, uint32_t mi,uint32_t s, \
				uint32_t ms): vec_msg(21,0), year(y), \
				month(mo), day(d), hour(h), minute(mi), \
				second(s), millisecond(ms) {

		uint8_t year_bH = (year>>8)&0xff;
		uint8_t year_bL = year&0xff;
		uint8_t month_b = month&0xff;
		uint8_t day_b = day&0xff;
		uint8_t hour_b = hour&0xff;
		uint8_t minute_b = minute&0xff;
		uint8_t second_b = second&0xff;
		uint8_t millisecond_bH = (millisecond>>8)&0xff;
		uint8_t millisecond_bL = millisecond&0xff;
		vec_msg = {0xbd,0xdb,0x53,0x0C,0x00,0xE8,0x03,0x00,0x00,year_bL,year_bH,month_b,day_b,hour_b,minute_b,second_b,millisecond_bL,millisecond_bH,0x99,0xAA,0x00};
        uint8_t checksum = 0x00;
        for(int j=0;j<19;j++)
        {
            checksum = msg[j]^checksum;
        }
		vec_msg[19] = checksum;
		vec_msg[20] = 0x00;

		std::copy(vec_msg.begin(), vec_msg.end(), msg);

	};

	void update();
	void dump_hex_msg();
	void __dump_hex_msg();
	void send_msg(int fd);
	friend std::ostream & operator << (std::ostream &, const c_utc_time &);

};

c_utc_time cur_time = {2021, 7, 31, 23, 59, 45, 10};

int main(int argc, char * argv[]) {

	enum enGPIO pin = pps_gpio;
	printf("wiring.pi gpio is %d\n", pin);
	
	if (wiringPiSetup() < 0){
		fprintf(stderr, "Cant setup wiringPi!\n");
		return errno;
	}

	if (wiringPiISR(pin, INT_EDGE_RISING, cb_pin_isr) < 0){
		fprintf(stderr, "Cant register pin isr!\n");
		return errno;
	}

	int fd;
	fd=serialOpen("/dev/ttyAMA0", 460800);	/*	53 uart init	*/
	if(fd==-1)
	{
		printf("open serial failed \n");
		return 0;
	}

	for(;;){
		usleep(1000);
		if (ISR::int_flag == 1){
			ISR::int_flag = 0;
			cur_time.update();
			// cur_time.dump_hex_msg();
			cur_time.__dump_hex_msg();
			std::cout << cur_time << std::endl;
			usleep(5000);
			cur_time.send_msg(fd);
			printf("int flag reset\n");
			fflush(stdout);
			if (ISR::counter >= 10){
				system("clear");
				ISR::counter = 0;
			}
		}
	}

	serialClose(fd);
	return 0;
}

std::ostream & operator << (std::ostream & stream, const c_utc_time & cur_time) {
	stream << "Time is " << cur_time.year << "." << cur_time.month \
		<< 	"." << cur_time.day << " " << cur_time.hour << ":" \
		<< cur_time.minute << ":" << cur_time.second << ":" \
		<< cur_time.millisecond;
	return stream;
}

void c_utc_time::update(){
	second++;
	if(second>=60)
	{ second = 0; minute++; if(minute>=60)
		{
			minute = 0;
			hour++;
			if(hour==24)
			{
				day++;
				hour = 0;
				if(year==2000&&month==2&&day>29)
				{
					month++;
					day=1;
				}
				if(year!=2000&&month==2&&day>28)
				{
					month++;
					day=1;
				}
				if((month==1|month==3|month==5|month==7|month==8|month==10) && day>31)
				{
					month++;
					day=1;
				}
				if((month==4|month==6|month==9|month==11) && day>30)
				{
					month++;
					day=1;
				}
				if(month==12 && day>31)
				{
					year++;
					month = 1;
					day=1;
				}
			}
		}
	}

	uint8_t year_bH = (year>>8)&0xff;
	uint8_t year_bL = year&0xff;
	uint8_t month_b = month&0xff;
	uint8_t day_b = day&0xff;
	uint8_t hour_b = hour&0xff;
	uint8_t minute_b = minute&0xff;
	uint8_t second_b = second&0xff;
	uint8_t millisecond_bH = (millisecond>>8)&0xff;
	uint8_t millisecond_bL = millisecond&0xff;
	vec_msg = {0xbd,0xdb,0x53,0x0C,0x00,0xE8,0x03,0x00,0x00,year_bL,\
			year_bH,month_b,day_b,hour_b,minute_b,second_b,millisecond_bL, \
			millisecond_bH,0x99,0xAA,0x00};
    uint8_t checksum = 0x00;
    for(int j=0;j<19;j++)
    {
        checksum = vec_msg[j]^checksum;
    }
	vec_msg[19] = checksum;
	vec_msg[20] = 0x00;

	std::copy(vec_msg.begin(), vec_msg.end(), msg);

};


void c_utc_time::dump_hex_msg(){
	for (const auto & s : vec_msg){
		printf("0x%x ", s);
	}
	printf("\n");
	fflush(stdout);
};

void c_utc_time::__dump_hex_msg(){
	for (int i = 0; i<21; i++){
		printf("0x%x ",msg[i] );
	}
	printf("\n");
	fflush(stdout);
};

void c_utc_time::send_msg(int fd){
	write(fd, msg, sizeof(msg));
}
